/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package jj2000.stg.ui;

import java.awt.Component;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.image.ColorModel;
import java.awt.image.ImageConsumer;
import java.awt.image.ImageProducer;
import java.util.Vector;
import jj2000.disp.BlkImgDataSrcImageProducer;
import jj2000.j2k.image.BlkImgDataSrc;
import jj2000.j2k.image.Coord;
import jj2000.j2k.image.DataBlkInt;
import jj2000.j2k.image.input.ImgReaderPPM;

/**
 *
 * @author teq
 */
public class DataBlkIntImageProducer implements ImageProducer {

    /** The list of image consumers for this image producer */
    private volatile Vector consumers;

    /** The source of image data */
    private DataBlkInt srcR;
    private DataBlkInt srcG;
    private DataBlkInt srcB;

    /** The type of image: GRAY, RGB or RGBA */
    private int type;

    /** The gray-level image type (256 levels). For this type the source of
     * image data must have only 1 component. */
    private static final int GRAY = 0;

    /** The color image type (24 bits RGB). No alpha plane. For this type the
     * source of image data must have 3 components, which are considered to be
     * R, G and B, in that order */
    private static final int RGB = 1;

    /** The color image type (32 bits RGBA). For this type the source of image
     * data must have 4 components, which are considered to be R, G, B and A,
     * in that order. */
    private static final int RGBA = 2;

    /** The default color model (0xAARRGGBB) used in Java */
    private static final ColorModel cm = ColorModel.getRGBdefault();

    /**
     * Creates an image producer which uses 'src' as the source of image
     * data. If 'once' is true then the image is produced only once.
     *
     * @param src The source of image data.
     *
     * @param once If the image is to be produced only once or not.
     * */
    public DataBlkIntImageProducer(DataBlkInt src) {

        type = GRAY;
        this.srcR = src;
        this.srcG = src;
        this.srcB = src;
        consumers = new Vector();
    }

    public DataBlkIntImageProducer(DataBlkInt pSrcR,
                                   DataBlkInt pSrcG,
                                   DataBlkInt pSrcB) {
        type = RGB;
        this.srcR = pSrcR;
        this.srcG = pSrcG;
        this.srcB = pSrcB;
        consumers = new Vector();
    }
    
    /**
     * Returns an Image object given an BlkImgDataSrc source. It will use a
     * new J2KImageProducer object as the underlying image producer.
     *
     * <p>This method uses the JVM default Toolkit, which might not be what it
     * is desired.</p>
     *
     * @param src The source of image data.
     *
     * @return An image which has a J2KImageProducer object as the underlying
     * image producer.
     * */
    public static Image createImage(DataBlkInt src) {
        // Use the system toolkit's createImage method
        return Toolkit.getDefaultToolkit().
            createImage(new DataBlkIntImageProducer(src));
    }
    
    public static Image createImage(DataBlkInt srcR,
                                    DataBlkInt srcG,
                                    DataBlkInt srcB) {
        // Use the system toolkit's createImage method
        return Toolkit.getDefaultToolkit().
            createImage(new DataBlkIntImageProducer(srcR, srcG, srcB));
    }

    /**
     * Returns an Image object given an BlkImgDataSrc source. It will use a
     * new J2KImageProducer object as the underlying image producer.
     *
     * <p>This method uses the component's toolkit. The toolkit of a component
     * may change if it is moved from one frame to another one, since it is
     * the frame that controls which toolkit is used.</p>
     *
     * @param src The source of image data.
     *
     * @param c The component to use to generate the 'Image' object from the
     * 'ImageProducer'.
     *
     * @return An image which has a J2KImageProducer object as the underlying
     * image producer.
     * */
    public static Image createImage(DataBlkInt src, Component c){
        // Use the component's toolkit createImage method
        return c.getToolkit().
            createImage(new DataBlkIntImageProducer(src));
    }

    public static Image createImage(DataBlkInt srcR,
                                    DataBlkInt srcG,
                                    DataBlkInt srcB,
                                    Component c){
        // Use the component's toolkit createImage method
        return c.getToolkit().
            createImage(new DataBlkIntImageProducer(srcR, srcG, srcB));
    }
    
    /**
     * Registers an image consumer with this image producer. The delivery of
     * image data does not start immediately. It will only start after the
     * next call to the startProduction() method.
     *
     * @param ic The image consumer to which image data has to be delivered.
     *
     * @see #startProduction
     * */
    public final synchronized void addConsumer(ImageConsumer ic) {
        if (ic != null && !consumers.contains(ic)) {
            consumers.addElement(ic);
        }
    }

    /**
     * This method determines if the given image consumer, 'ic', is registered
     * with this image producer.
     *
     * @param ic The image consumer to test.
     *
     * @return True if 'ic' is registered with this image producer, false
     * otherwise.
     * */
    public boolean isConsumer(ImageConsumer ic) {
        return consumers.contains(ic);
    }

    /**
     * Removes the given image consumer 'ic' from the list of consumers
     * registered with this producer. This image producer will stop sending
     * pixel data to 'ic' as soon as it is feasible. The method call is
     * ignored if 'ic' has not been registered with this image producer.
     *
     * @param ic The image consumer to be removed
     * */
    public synchronized void removeConsumer(ImageConsumer ic) {
        consumers.removeElement(ic);
    }

    /**
     * Registers the given ImageConsumer object as a consumer and starts an
     * immediate reconstruction of the image data which will then be delivered
     * to this consumer and any other consumer which may have already been
     * registered with the producer.
     *
     * <p>Delivery is performed in "parallel" to all the registered image
     * consumers. By "parallel" it is meant that each line of the image is
     * delivered to all consumers before delivering the next line.</p>
     *
     * <p>If the data returned by the BlkImgDataSrc source happens to be
     * progressive (see BlkImgDataSrc and DataBlk) then the abort condition is
     * sent to the image consumers and no further data is delivered.</p>
     *
     * <p>Once all the data is sent to a consumer this one is automatically
     * removed from the list of registered ones, unless an abort happens.</p>
     *
     * <p>To start the BlkImgDataSrc is set to tile (0,0), and the tiles are
     * produced in raster sacn order. Once the last tile is produced,
     * setTile(0,0) is called again, which signals that we are done with the
     * current tile, which might free up resources.</p>
     *
     * @param ic The image consumer to register
     * */
    public void startProduction(ImageConsumer ic) {
        
        final ImageConsumer[] cons;    // image consumers cache
        int i, hints;                     // hints to image consumers

        // Register ic
        if (ic != null) {
            addConsumer(ic);
        }

        // Set the cache for the consumers
        synchronized (this) {
            // synchronized to avoid addition or deletion of consumers while
            // copying them to cache
            cons = new ImageConsumer[consumers.size()];
            consumers.copyInto(cons);
        }

        if(srcR == null || srcG == null || srcB == null) {
            // We cant't render with no source
            for (i=cons.length-1; i>=0; i--) {
                cons[i].imageComplete(ImageConsumer.IMAGEERROR);
            }
            return; // can not continue processing
        }

        // Set the hints and info to the cached consumers
        hints = ImageConsumer.SINGLEFRAME|ImageConsumer.SINGLEPASS|
                ImageConsumer.COMPLETESCANLINES|ImageConsumer.TOPDOWNLEFTRIGHT;
        for (i=cons.length-1; i>=0; i--) {
            cons[i].setColorModel(cm);
            cons[i].setDimensions(srcR.w, srcR.h);
            cons[i].setHints(hints);
        }

        int[] pixbuf = new int[srcR.data.length];
        
        if (type == GRAY) {
            for (i = 0; i < pixbuf.length; i++) {
                pixbuf[i] = (0xFF << 24)
                            | ((srcR.data[i] + ImgReaderPPM.DC_OFFSET) << 16)
                            | ((srcR.data[i] + ImgReaderPPM.DC_OFFSET) << 8)
                            | (srcR.data[i] + ImgReaderPPM.DC_OFFSET);
            }
        } else if (type == RGB)
        {
            for (i = 0; i < pixbuf.length; i++) {
                pixbuf[i] = (0xFF << 24)
                            | ((srcR.data[i] + ImgReaderPPM.DC_OFFSET) << 16)
                            | ((srcG.data[i] + ImgReaderPPM.DC_OFFSET) << 8)
                            | (srcB.data[i] + ImgReaderPPM.DC_OFFSET);
            }
        } else {
        }
        
        
        // Start the data delivery to the cached consumers
        for (i=cons.length-1; i>=0; i--) {
            cons[i].setPixels(0, 0, srcR.w, srcR.h, cm, pixbuf, 0, srcR.w);
        }
        
        // Signal that this frame is complete. This is so that display of the
        // last tile occurs as soon as possible. When calling with
        // STATICIMAGEDONE the ImageConsumer might do some cleanup that will
        // take a considerable amount of time on large images, this is why we
        // first signal the frame as done (even though there is only one).
        for (i=cons.length-1; i>=0; i--) {
            cons[i].imageComplete(ImageConsumer.SINGLEFRAMEDONE);
        }
        // Signal that image is complete
        for (i=cons.length-1; i>=0; i--) {
            cons[i].imageComplete(ImageConsumer.STATICIMAGEDONE);
        }
        // Remove the consumers since all the data has been sent
        synchronized (this) {
            for (i=cons.length-1; i>=0; i--) {
                consumers.removeElement(cons[i]);
            }
        }
    }

    /**
     * Starts the delivery of pixel data in the top-down letf-right order to
     * the image consumer 'ic'. The TOPDOWNLEFTRIGHT hint is set in the image
     * consumer on delivery.
     *
     * <p>Currently this call is ignored (which is perfectly legal according
     * to the ImageProducer interface specification).</p>
     *
     * @param ic The image consumer to which the data is sent in top-down,
     * left-right order.
     * */
    public void requestTopDownLeftRightResend(ImageConsumer ic) { }
}
